﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

// Reviewed
#pragma once
#include "GcmpCommandAction.h"
#include "ContainerUtil.h"
#include "GcmpApiDeclaration.h"
#include "IAction.h"

namespace gcmp
{
    class Vector3d;
    class Coordinate3d;
    class Matrix4d;
    class IUiView;
    class IPickFilter;
    class ISnapContext;
    class IPickEventHandler;

    /// \brief 拾取回调接口，通常用于临时绘制临时对象，如橡皮筋
    ///
    class IPickCallback
    {
    public:
        virtual ~IPickCallback() {}

        /// \brief 移动鼠标
        /// \param pCurrentView 当前视图
        /// \param pickedPt  拾取点
        /// \param mousePt   鼠标点
        /// \return 
        virtual void OnMouseMove(IUiView* pCurrentView, const Vector3d &pickedPt,const Vector3d& mousePt) = 0;

    };

    /// \brief 交互式拾取
    ///
    class GCMP_COMMAND_ACTION_EXPORT InteractivePicker
    {
    public:
        /// \brief 交互式拾取执行结果状态
        ///
        enum class PickerFinishStatus : int32_t
        {
            Unfinished,   ///< 默认初始状态
            Successful,   ///< 成功执行完成
            Cancelled     ///< 取消状态（一般是被用户按Esc或Enter取消了）
        };

        /// \brief 交互式拾取退出的原因
        ///
        enum class PickerCancelReason : int32_t
        {
            Unknown,
            Esc,          ///< 用户按Esc键
            Enter,        ///< 用户按Esc键
            RButtonDown,  ///< 用户点击鼠标右键(一般是弹出右键，只有指定右键退出才生效)
            RButtonUp,     ///< 用户点击鼠标右键(一般是弹出右键，只有指定右键退出才生效)
            UserInputString, ///< 用户输入的字符串
            UserCustomReason  ///< 自定义原因
        };

        /// \brief 拾取点的类型
        enum class PickPointType : Int32
        {
            TwoDimensionPoint = 0, /// 要求拾取的是二维点，即最终返回的结果只有x，y值有效，z值无效，但可以获取到值，值始终为0
            ThreeDimensionPoint /// 要求拾取的是三维点，即最终返回的结果x，y，z均有效，默认拾取的是三维点
        };

        /// \brief 捕捉平面的类型
        enum class SnapPlaneType : Int32
        {
            WorkPlane = 0,                      /// < 使用工作平面
            FirstPointPlane = 1,                /// < 根据当前Camera方向构造平面,第一个捕捉输入点为该平面原点
            SnapPlane = 2,                      /// < 用户自定义的捕捉平面
        };

        /// \brief 交互式拾取结果
        struct PickerResult
        {
            PickerResult()
                :FinishStatus(PickerFinishStatus::Unfinished)
                , CancelReason(PickerCancelReason::Esc)
                , ActionOutputData(nullptr)
            {

            }
            PickerResult(const PickerResult& obj)
                : FinishStatus(obj.FinishStatus)
                , CancelReason(obj.CancelReason)
                , UserInputMessage(obj.UserInputMessage)
                , ActionOutputData(nullptr)
            {
                if (obj.ActionOutputData != nullptr)
                {
                    ActionOutputData = TransferOwnership(obj.ActionOutputData->Clone());
                }
            }
            PickerResult& operator=(const PickerResult &obj)
            {
                if (this == &obj)
                    return *this;
                FinishStatus = obj.FinishStatus;
                CancelReason = obj.CancelReason;
                UserInputMessage = obj.UserInputMessage;
                ActionOutputData = nullptr;
                if (obj.ActionOutputData != nullptr)
                    ActionOutputData = TransferOwnership(obj.ActionOutputData->Clone());
                return *this;
            }
            PickerFinishStatus FinishStatus; ///< 结束状态 
            PickerCancelReason CancelReason; ///< 退出原因，如ESC键或鼠标右键
            std::wstring UserInputMessage;   ///< 用户输入字符串信息
            OwnerPtr<IActionOutputData> ActionOutputData; ///< 返回数据
        };
    public:
        /// \brief 选择
        /// \param isMultiSelect 是否多选
        /// \param isPickPointSelection  是否点选，多选时有效
        /// \param opickFilter   选择过滤器
        /// \param mouseTooltip  鼠标提示信息
        /// \param statusBarTooltip 状态栏提示信息
        /// \param mousePoint    鼠标点
        /// \param nodeReferences 选择集合
        /// \param isRButtonCanceled 是否右键退出
        /// \param opPickPostEventHandler 选择后事件处理器
        /// \return 拾取成功和退出状态
        static PickerResult Select(bool isMultiSelect, bool isPickPointSelection, OwnerPtr<IPickFilter> opPickFilter, const std::wstring& mouseTooltip,
            const std::wstring& statusBarTooltip, Vector3d& mousePoint, gcmp::GraphicsNodeReferenceOwnerPtrVector &nodeReferences, bool isRButtonCanceled = false,
            OwnerPtr<IPickEventHandler> opPickPostEventHandler = nullptr);

        /// \brief 捕捉点
        /// \param pSnapContext 捕捉上下文
        /// \param pBasePoint    捕捉开始基点，可以为nullptr
        /// \param mouseTooltip  鼠标提示信息
        /// \param statusBarTooltip 状态栏提示信息
        /// \param opPickCallBack 鼠标捕捉时回调函数，当基点不为空有效
        /// \param selectedPoint  捕捉点
        /// \param isRButtonCanceled 是否右键退出
        /// \param snapPlaneType 捕捉平面类型
        /// \param pickPointType 拾取点类型
        /// \return 获取点成功和退出状态 
        static PickerResult GetPoint(const ISnapContext* pSnapContext, const Vector3d *pBasePoint, const std::wstring& mouseTooltip,
            const std::wstring& statusBarTooltip, OwnerPtr<IPickCallback> opPickCallBack,Vector3d &selectedPoint,bool isRButtonCanceled = false, 
            SnapPlaneType snapPlaneType = SnapPlaneType::FirstPointPlane,
            PickPointType pickPointType = PickPointType::ThreeDimensionPoint);

        /// \brief 获取罗盘变换信息
        /// \param coordinate 罗盘坐标系
        /// \param matrix     罗盘变换矩阵
        /// \return 获取罗盘变换信息成功和退出状态
        static PickerResult GetLocatorTransformation(const gcmp::Coordinate3d &coordinate, gcmp::Matrix4d &matrix);

        /// \brief 退出当前交互操作
        /// \param finishStatus  当前Action的结束状态
        /// \param reason        当前Action的结束原因（只有在枚举是UserCustomReason时，才能结束 GetLocatorTransformation函数，其他情况下默认不结束GetLocatorTransformation函数）
        /// \param outputData    当前Action结束时的额外输出数据，会在PickerResult中获取到
        /// \return
        static void EndCurrentInteractivePicker(ActionFinishStatus finishStatus = ActionFinishStatus::Cancelled, ActionFinishReason reason = ActionFinishReason::Unknown , OwnerPtr<IActionOutputData> outputData = nullptr);
    };
}


